#version 440 core

#define LOC_SIZE 64

#define GRID_SIZE_X 64
#define GRID_SIZE_Y 64

uniform uint numActiveNodes;
uniform uint findNextActiveNodes;

layout(local_size_x=LOC_SIZE) in;

layout(binding=0, r32ui) uniform coherent uimage2D levelSetStateImage;
layout(binding=0) uniform sampler2D levelSetTextureSrc;

layout(binding=0, offset=0) uniform atomic_uint nextActiveNodesCounter;

layout(std430, binding=0) buffer activeNodesBuffer
{
	ivec2 activeNodes[];
};

layout(std430, binding=1) buffer nextActiveNodesBuffer
{
	ivec2 nextActiveNodes[];
};

layout(std430, binding=2) buffer activeNodeLevelSetBuffer
{
	float activeNodeLevelSetValues[];
};

void main(void)
{
	uint gid = gl_GlobalInvocationID.x;
	if (gid < numActiveNodes)
	{
		ivec2 nodePosition = activeNodes[gid];

		vec4 levelSetValues = textureGather(levelSetTextureSrc, (nodePosition+vec2(0.0,1.0))*vec2(1.0/(GRID_SIZE_X+1.0), 1.0/(GRID_SIZE_Y+1.0)));
		float levelLeft = levelSetValues.w;
		float levelTop = levelSetValues.y;
		levelSetValues = textureGather(levelSetTextureSrc, (nodePosition+vec2(1.0,0.0))*vec2(1.0/(GRID_SIZE_X+1.0), 1.0/(GRID_SIZE_Y+1.0)));
		float levelRight = levelSetValues.y;
		float levelBottom = levelSetValues.w;
		
		float levelHor = min(abs(levelLeft), abs(levelRight));
		float levelVer = min(abs(levelTop), abs(levelBottom));
		
		float levelSign = mix(1.0, -1.0, levelLeft < 0 || levelRight < 0 || levelTop < 0 || levelBottom < 0);

		float levelCenter = mix(
			0.5 * (levelHor + levelVer + sqrt(2 + 2*levelHor*levelVer - levelHor*levelHor - levelVer*levelVer)),
			min(levelHor, levelVer)+1,
			abs(levelHor-levelVer) > 1
		);
		
		activeNodeLevelSetValues[gid] = levelCenter*levelSign;
		
		if (findNextActiveNodes == 1)
		{
			ivec2 neighborPositions[4] = {
				clamp(nodePosition+ivec2(-1,0), ivec2(0,0), ivec2(GRID_SIZE_X, GRID_SIZE_Y)),
				clamp(nodePosition+ivec2(1,0), ivec2(0,0), ivec2(GRID_SIZE_X, GRID_SIZE_Y)),
				clamp(nodePosition+ivec2(0,-1), ivec2(0,0), ivec2(GRID_SIZE_X, GRID_SIZE_Y)),
				clamp(nodePosition+ivec2(0,1), ivec2(0,0), ivec2(GRID_SIZE_X, GRID_SIZE_Y))
			};
			for (uint i=0; i<4; i++)
			{
				if (imageAtomicExchange(levelSetStateImage, neighborPositions[i], 1u) == 0)
				{
					uint index = atomicCounterIncrement(nextActiveNodesCounter);
					nextActiveNodes[index] = neighborPositions[i];
				}
			}
		}
	}
}